package com.itheima.service;


import cn.hutool.core.collection.CollUtil;
import com.itheima.api.VisitorsApi;
import com.itheima.vo.PageResult;
import com.itheima.api.MovementApi;
import com.itheima.api.UserInfoApi;
import com.itheima.vo.MovementsVo;
import com.itheima.domain.UserInfo;
import com.itheima.exception.BusinessException;
import com.itheima.interceptor.UserHolder;
import com.itheima.mongo.Movement;
import com.itheima.server.ErrorResult;
import com.itheima.template.OssTemplate;
import com.itheima.utils.Constants;
import com.itheima.vo.Visitors;
import com.itheima.vo.VisitorsVo;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

//与朋友圈相关的服务层类
@Service
public class MovementService {

    @Autowired
    private OssTemplate ossTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @DubboReference
    private MovementApi movementApi;

    @DubboReference
    private UserInfoApi userInfoApi;

    @DubboReference
    private VisitorsApi visitorsApi;


    /**
     * 发布动态
     */
    //moveement 获取前端传送过来的动态内容  MultipartFile[]上传的多个文件 因为前端传过来的是from表单结构 所以无需使用@RequestBody 转js
    public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException {
        //1、判断发布动态的内容是否存在
        //通过String.Utils.isEmpty 方法判断是否为空
        //通过movement动态表获取 文字内容TextContent
        if (StringUtils.isEmpty(movement.getTextContent())) {
            //如果为空直接抛出业务异常处理
            throw new BusinessException(ErrorResult.contentError());
        }
        //不为空继续执行
        //2、获取当前登录的用户id
        //UserHolder.getUserId从当前线程，获取用户对象的id
        Long userId = UserHolder.getUserId();
        //3、将文件内容上传到阿里云OSS，获取请求地址
        List<String> medias = new ArrayList<>();
        for (MultipartFile multipartFile : imageContent) {
            //通过ossTemplate.upload 获取文件的名称 和输入流 获取请求地址
            String upload = ossTemplate.upload(multipartFile.getOriginalFilename(), multipartFile.getInputStream());
            //得到所有的请求地址
            medias.add(upload);
        }
        //4、将数据封装到Movement对象
        //动态表里 用户id
        movement.setUserId(userId);
        //用户上传的数据
        movement.setMedias(medias);
        //调用动态表api 发布方法 发布动态
        movementApi.publish(movement);
    }

    /**
     * 查询个人动态
     */

    public PageResult findByUserId(Long userId, Integer page, Integer pagesize) {
        //1、根据用户id，调用API查询个人动态内容（PageResult  -- Movement）
        //pr 得到查询到的 个人动态 movementApi.findByUserId
        PageResult pr = movementApi.findByUserId(userId, page, pagesize);
        //2、获取PageResult中的item列表对象
        //得到该用户的所有动态信息 储存到动态列表里
        List<Movement> items = (List<Movement>) pr.getItems();
        //3、非空判断
        if (items == null) {
            return pr;
        }
        //4、循环数据列表
        //通过id接口 查询到用户详情
        UserInfo userInfo = userInfoApi.findById(userId);
        List<MovementsVo> vos = new ArrayList<>();
        //增强for 遍历每一个动态信息
        for (Movement item : items) {
            //5、一个Movement构建一个Vo对象
            //通过 init 方法 将用户详细信息 和 动态组合起来
            MovementsVo vo = MovementsVo.init(userInfo, item);
            //添加到 集合中
            vos.add(vo);
        }
        //6、构建返回值
        //返回完整的 查询到的个人动态页面
        pr.setItems(vos);
        return pr;
    }

    /**
     * 查询好友动态
     *
     * @param page
     * @param pagesize
     * @return
     */
    public PageResult findFriendMovements(Integer page, Integer pagesize) {
        //获取当前用户id
        Long userId = UserHolder.getUserId();
        //调用API查询当前用户好友发布的动态列表
        List<Movement> list = movementApi.findFriendMovements(page, pagesize, userId);

        return getPageResult(page, pagesize, list);
    }

    //抽取公共方法
    //通过findFriendMovements 传过来 所有好友动态
    private PageResult getPageResult(Integer page, Integer pagesize, List<Movement> list) {
        //判断列表是否为空
        if (CollUtil.isEmpty(list)) {
            return new PageResult();
        }
        //提取动态发布人的id列表 获取发动态的 好友id        集合           要获取的字段名   字段名返回的类型
        List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class);
        //根据用户的id列表获取用户详情 获取到所有好友信息详情
        Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, null);
        //一个Movement构造一个vo对象
        List<MovementsVo> vos = new ArrayList<>();
        for (Movement movement : list) {
            //通过for增强循环 获取到 好友发布的动态
            UserInfo userInfo = map.get(movement.getUserId());
            //判断好友用户信息是否为空
            if (userInfo != null) {
                //将好友信息和好友动态 组合
                MovementsVo vo = MovementsVo.init(userInfo, movement);
                //添加到集合中
                vos.add(vo);
            }
        }
        //构造PageResult并返回
        return new PageResult(page, pagesize, 0l, vos);
    }

    /**
     * 查询推荐动态
     *
     * @param page
     * @param pagesize
     * @return
     */
    public PageResult findRecommendMovements(Integer page, Integer pagesize) {
        //1、从redis中获取推荐数据  从线程中获取用户id 给该用户推荐动态
        String redisKey = Constants.MOVEMENTS_RECOMMEND + UserHolder.getUserId();
        //获取该用户的推荐动态
        String redisValue = (String) redisTemplate.opsForValue().get(redisKey);
        //2、判断推荐数据是否存在
        //创建一个空表单
        List<Movement> list = Collections.EMPTY_LIST;
        if (StringUtils.isEmpty(redisValue)) {
            //3、如果不存在，调用API随机构造10条动态数据 存入空表单中
            list = movementApi.randomMovements(pagesize);
        } else {
            //4、如果存在，处理pid数据   "16,17,18,19,20,21,10015,10020,10040,10064,10092,10093,10099,10067" 15
            //获取缓存动态数据 以，号分隔
            String[] values = redisValue.split(",");
            //判断当前页的起始条数是否小于数组总数
            if ((page - 1) * pagesize < values.length) {
                List<Long> pids = Arrays.stream(values).skip((page - 1) * pagesize).limit(pagesize)
                        .map(e -> Long.valueOf(e))
                        .collect(Collectors.toList());
                //5、调用API根据PID数组查询动态数据 添加到空表单
                list = movementApi.findMovementsByPids(pids);
            }
        }
        //6、调用公共方法构造返回值 页数 总页数 推荐动态
        return getPageResult(page, pagesize, list);
    }

    //根据id查询
    public MovementsVo findById(String movementId) {
        //1、调用api根据id查询动态详情
        Movement movement = movementApi.findById(movementId);
        //2、转化vo对象
        if (movement != null) {
            UserInfo userInfo = userInfoApi.findById(movement.getUserId());
            return MovementsVo.init(userInfo, movement);
        } else {
            return null;
        }
    }

    //首页-访客列表
    public List<VisitorsVo> queryVisitorsList() {
        //查询访问时间
        String key = Constants.VISITORS_USER;
        String hashKey = String.valueOf(UserHolder.getUserId());
        String value = (String) redisTemplate.opsForHash().get(key, hashKey);
        Long date = StringUtils.isEmpty(value) ? null : Long.valueOf(value);
        //调用API查询数据列表 List<Visitors>
        List<Visitors> list = visitorsApi.queryMyVisitors(date, UserHolder.getUserId());
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        //提取用户的id
        List<Long> userIds = CollUtil.getFieldValues(list, "visitorUserId", Long.class);
        //查看用户详情
        Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, null);
        //构造返回
        List<VisitorsVo> vos = new ArrayList<>();
        for (Visitors visitors : list) {
            UserInfo userInfo = map.get(visitors.getVisitorUserId());
            if (userInfo != null) {
                VisitorsVo vo = VisitorsVo.init(userInfo, visitors);
                vos.add(vo);
            }
        }
        redisTemplate.opsForHash().put(key, hashKey, System.currentTimeMillis());
        return vos;
    }
}
